/* Copyright (c) 2021 OceanBase and/or its affiliates. All rights reserved.
miniob is licensed under Mulan PSL v2.
You can use this software according to the terms and conditions of the Mulan PSL v2.
You may obtain a copy of Mulan PSL v2 at:
         http://license.coscl.org.cn/MulanPSL2
THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
See the Mulan PSL v2 for more details. */

//
// Created by Wangyunlai on 2022/5/22.
//

#include "sql/stmt/update_stmt.h"
#include "sql/stmt/select_stmt.h"
#include "common/log/log.h"
#include "sql/expr/expression.h"
#include "sql/stmt/filter_stmt.h"
#include "sql/stmt/stmt.h"
#include "storage/db/db.h"
#include "storage/table/table.h"
#include <memory>

UpdateStmt::UpdateStmt(
    Table *table, vector<std::unique_ptr<Expression>> &&value, vector<FieldMeta *> field_meta, FilterStmt *filter_stmt)
    : table_(table), value_(std::move(value)), field_meta_(field_meta), filter_stmt_(filter_stmt)
{}

UpdateStmt::~UpdateStmt()
{
  if (nullptr != filter_stmt_) {
    delete filter_stmt_;
    filter_stmt_ = nullptr;
  }
}

RC UpdateStmt::create(Db *db, UpdateSqlNode &update, Stmt *&stmt)
{
  const char *table_name = update.relation_name.c_str();
  if (nullptr == db || nullptr == table_name || update.attribute_name.size() == 0) {
    LOG_WARN("invalid argument. db=%p, table_name=%p, attr_size=%d",
        db, table_name, update.attribute_name.size());
    return RC::INVALID_ARGUMENT;
  }

  // check whether the table exists
  Table *table = db->find_table(table_name);
  if (nullptr == table) {
    LOG_WARN("no such table. db=%s, table_name=%s", db->name(), table_name);
    return RC::SCHEMA_TABLE_NOT_EXIST;
  }

  // check whether the field exists
  vector<FieldMeta *>            field_meta;
  vector<unique_ptr<Expression>> values;
  for (unsigned i = 0; i < update.attribute_name.size(); i++) {
    const char      *attr_name     = update.attribute_name[i].c_str();
    const FieldMeta *my_field_meta = table->table_meta().field(attr_name);
    if (nullptr == my_field_meta) {
      LOG_WARN("no such field in table. db=%s, table=%s, field name=%s",
             db->name(), table_name, update.attribute_name[i].c_str());
      return RC::SCHEMA_FIELD_NOT_EXIST;
    }
    FieldMeta *reg_field_meta = new FieldMeta(*my_field_meta);
    field_meta.push_back(reg_field_meta);
    values.emplace_back(update.value_exprs[i]);
    // check whether the type of field match with that of type
    if (update.value_exprs[i]->type() == ExprType::VALUE) {
      Value val = static_cast<ValueExpr *>(update.value_exprs[i])->get_value();
      if (val.attr_type() == AttrType::CHARS && val.length() > 65535) {
        LOG_WARN("the length of char type value is too long. max length is 65535");
        return RC::INVALID_ARGUMENT;
      }

      // 下面这里的逻辑转移到了physical阶段再检查，因为要和子查询逻辑做一个统一
      // if (reg_field_meta->type() != val.attr_type()) {
      //   LOG_WARN("Invalid argument due to type not match!");
      //   return RC::INVALID_ARGUMENT;
      // }
    } else if (update.value_exprs[i]->type() == ExprType::SUB_QUERY) {
      RC rc2 = RC::SUCCESS;
      // 下面这里应该将stmt转换为逻辑算子
      // 现在已经有了sql_node
      // TODO:
      // BUG:子查询的表不能是外层查询的表
      //
      SubQueryExpr *sub_query_expr = static_cast<SubQueryExpr *>(update.value_exprs[i]);
      // 这里先判断一下子查询的表是不是外层的表
      for (auto item : sub_query_expr->get_sql_node()->relations_and_aliases) {
        if (item.first == update.relation_name) {
          LOG_WARN("sub query must be different from that outside");
          return RC::INTERNAL;
        }
      }
      SelectStmt select_stmt;
      Stmt      *stmt = nullptr;
      rc2             = select_stmt.create(db, *sub_query_expr->get_sql_node(), stmt);
      // 这里已经拿到了stmt
      if (rc2 != RC::SUCCESS) {
        LOG_WARN("failed to get select stmt in sub query");
        return RC::INTERNAL;
      }
      if (stmt->type() != StmtType::SELECT) {
        LOG_WARN("wrong stmt type");
        return RC::INVALID_ARGUMENT;
      }
      // 下面将它转换为select_stmt
      SelectStmt *ss = static_cast<SelectStmt *>(stmt);
      // 这里把select_stmt传递给expr
      sub_query_expr->set_select_stmt(ss);
    } else {
      LOG_WARN("nothing to do now!");
      return RC::INTERNAL;
    }
  }

  std::unordered_map<std::string, Table *> table_map;
  table_map.insert(std::pair<std::string, Table *>(std::string(table_name), table));

  FilterStmt *filter_stmt = nullptr;
  RC          rc          = FilterStmt::create(
      db, table, &table_map, update.conditions.data(), static_cast<int>(update.conditions.size()), filter_stmt);
  if (rc != RC::SUCCESS) {
    LOG_WARN("failed to create filter statement. rc=%d:%s", rc, strrc(rc));
    return rc;
  }
  // everything alright
  update.value_exprs.clear();
  stmt = new UpdateStmt(table, std::move(values), field_meta, filter_stmt);
  return RC::SUCCESS;
}
